package db_main

import (
	"context"
	"errors"
	"fmt"
	global2 "longmen/server/config/global"
	_const "longmen/server/pkg/common/helper/const"
	utils2 "longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/db/mysql"
	"longmen/server/pkg/db/mysql/models/models_main"
	"math"
	"strconv"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

/*
@Modify: 2022 Jun 29 Enzo
CreateFlow这一块已交给NATS处理；现在这个方法只用于不涉及双方交易的地方（当用户和系统对手时）；
未来进一步可优化:用户余额放入缓存，每隔n秒把用户余额写入库，并在库里写入updateTime, last transtime
*/
func UpdateAmount(uid int, amount int, amountType _const.FundsType, consumeType _const.FundsType) (beforeAmount int, afterAmount int, err error) {
	u := models_main.User{}
	lock := clause.Locking{
		Strength: "UPDATE",
		Options:  "NOWAIT",
	}
	err = global2.GetX().Clauses(lock).Where("id = ?", uid).Find(&u).Error
	if err != nil {
		return 0, 0, err
	}
	if consumeType == _const.PROFIT_COMMISSION_ANCHOR &&
		u.AnchorType == 1 && amountType == _const.CoinDiamond {
		global2.Log.Debugf("profitDiamond累加：%v", u)
		u.ProfitDiamond += amount //只有主播，收入类型是收入，这里才会加
	}
	tx := global2.GetX().Begin()
	if amountType == _const.CoinDiamond {
		beforeAmount = u.Diamond
	} else {
		beforeAmount = u.Gold
	}
	global2.Log.Debugf("礼物类型：%v", amountType)
	if amount > 0 {
		afterAmount = beforeAmount + int(math.Abs(float64(amount)))
	} else {
		afterAmount = beforeAmount - int(math.Abs(float64(amount)))
	}
	if afterAmount < 0 {
		tx.Rollback()
		return 0, 0, errors.New(global2.Lang.T("insufficient_balance"))
	}
	if amount > 0 {
		u.DiamondTotal += amount
	}
	var up *gorm.DB
	if amountType == _const.CoinDiamond {
		up = tx.Model(&u).Updates(map[string]interface{}{
			"diamond":        afterAmount,
			"profit_diamond": u.ProfitDiamond,
			"diamond_total":  u.DiamondTotal,
		})
	} else {
		up = tx.Model(&u).Updates(map[string]interface{}{"gold": afterAmount})
	}
	if up.RowsAffected == 0 {
		tx.Rollback()
		return 0, 0, errors.New("更新失败")
	}
	err = up.Error

	if err != nil {
		tx.Rollback()
		return 0, 0, err
	}
	err = tx.Commit().Error

	return beforeAmount, afterAmount, err
}

/*
*
* @Author:William
* @Description: 上面方法的复制版 只增加了一个传入session
* @Param :
* @Return :
* @Date: 17:52 2022/6/17
这个方法用于大部分的交易，比如给A加钱时同时要给B扣钱，还要记一笔A和B交易的记录；这三笔是一起成功、一起失败的；
*/
func UpdateAmountByTx(uid int, amount int, amountType _const.FundsType, session *gorm.DB, showBlance int, profitLog []*models_main.UserProfit) (beforeAmount int, afterAmount int, err error) {
	//是0不需要更新
	if amount == 0 {
		return 0, 0, errors.New("账变为0，更新失败")
	}
	lock := clause.Locking{
		Strength: "UPDATE",
		Options:  "NOWAIT",
	}
	var sql string
	u := models_main.User{}
	err = session.Where("id = ?", uid).Clauses(lock).Find(&u).Error
	if amountType == _const.CoinDiamond {
		beforeAmount = u.Diamond
		sql = fmt.Sprintf("update db_user set diamond=diamond+? where id=? ")
	} else {
		beforeAmount = u.Gold
		sql = fmt.Sprintf("update db_user set gold=gold+? where id=?")
	}
	afterAmount = beforeAmount + amount
	if afterAmount < 0 {
		session.Rollback()
		return 0, 0, errors.New(global2.Lang.T("insufficient_balance_or_invalid_amount"))
	}
	exec := session.Exec(sql, amount, uid)
	if exec.Error != nil || exec.RowsAffected == 0 {
		session.Rollback()
		global2.Log.Errorf("6Account96UpdateAmount9余额不足或金额无效%s,%d,%d,%d", sql, amount, uid, amount)
		return 0, 0, errors.New(global2.Lang.T("insufficient_balance_or_invalid_amount"))
	}
	if profitLog != nil { //如果传入的是nil则不用记录
		if err := InsertUserProfitByTransactions(session, profitLog); err != nil {
			session.Rollback()
			global2.Log.Error("写入交易记录失败:%+v", profitLog)
			return 0, 0, err
		}
	}
	return beforeAmount, afterAmount, nil
}

func GetBalance(uid int) (int, int, error) {
	u := models_main.User{}
	//lock := clause.Locking{
	//	Strength: "UPDATE",
	//	//Options:  "NOWAIT",
	//}
	err := global2.GetX().Where("id = ?", uid).Find(&u).Error
	if err != nil {
		global2.Log.Errorf("6GetBalance9:%v", err)
		return 0, 0, err
	}
	return u.Diamond, u.Gold, nil
}

/*
todo 转移到nats中
*/
func CreateFlow(session *gorm.DB, uid int, amount int, fundsType _const.FundsType) error {
	flow := &models_main.FundsFlow{
		Uid:        uid,
		Amount:     amount,
		Type:       uint8(fundsType),
		CreateTime: time.Now(),
	}
	err := session.Create(flow).Error
	if err != nil {
		return err
	}
	return nil
	//return errors.New("wrong")
}

/**
 * @Author admin
 * @Description // 红包批量插入定制版
 * @Date 11:12 2022/7/10
 * @Param
 * @return
 **/
func BatchFlowHongBao(session *gorm.DB) error {
	sql := fmt.Sprintf("insert into db_funds_flow(uid,amount,type,create_time,consume_type) select user_id,grab_diamond,2,now(),11 from db_diamond_temp")
	return session.Exec(sql).Error

	//return errors.New("wrong")
}

/**
*Create: Enzo 2022-Jun-29
* @Description:插入一条新的funds flow 这个是worker在收到nats消息后做的
*Parameters: models.FundsFlow {uid, amount金额，fundsType: 1 鹰钻 2 克拉, consumeType: 消费类型}
 */

func InsertFlow(flow *models_main.FundsFlow) error {
	fName := "InsertFlow"
	global2.Log.Debugf("%s%v", fName, flow)
	table := mysql.GetRTable(&models_main.FundsFlow{})
	err := table.Create(flow).Error

	if err != nil {
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	return nil
}

/**
*@Create Enzo 2022-Jun-30
*将"funds flow"流水记录 insert to ElasticSearch
*todo: 留意读取的业务
 */
func FundsFlowToES(ff *models_main.FundsFlow) error {
	fName := "FundsFlowToES"
	resp, err := global2.GetEs().Index().Index(_const.ES_FUNDS_FLOW).
		Id(strconv.FormatInt(ff.Id, 10)).
		BodyJson(ff).
		Do(context.Background())
	if err != nil {
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	global2.Log.Debug("insert fund flow to ES resp = ", resp)
	return nil
}

/*
*
*@Create Enzo 2022-July-04
*发生了送礼事件，在这里要写入主记录：修改用户余额、主播余额、送礼记录
*目前主记录是在MYSQL里的，其他的分成、流水、用户profit表，都是辅记录；
将来可能要把主记录改到缓存里；考虑送礼的并发；
*/
func ProcessSendGift(gift *models_main.GiftLog) (int, error) {
	//开始支付 开始事务
	session := global2.GetX().Begin()
	var err error
	//扣送礼人的钱
	if gift.Spend != 0 { //活动礼物扣款为0
		_, _, err = UpdateAmountByTx(gift.Uid, -gift.ActualSpend,
			_const.FundsType(gift.SpendType), session, _const.WALLET_BALANCE_HIDE,
			nil)
		if err != nil {
			return 0, err
		}
	}
	//加收礼人的钱
	_, _, err = UpdateAmountByTx(gift.Anchorid, gift.AnchorProfit,
		_const.FundsType(gift.SpendType), session, _const.WALLET_BALANCE_HIDE,
		nil)
	if err != nil {
		return 0, err
	}
	giftLogId, err := InsertLogByTx(session, gift)
	//提交事务
	if err := session.Commit().Error; err != nil {
		return 0, err
	}
	return giftLogId, nil
}
